package org.example.rzfx.task;

import org.example.rzfx.entity.Log;
import org.example.rzfx.service.LogCacheService;
import org.example.rzfx.service.LogService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 日志回写任务
 * 
 * 功能：
 * 1. 定时从Redis读取缓存的日志
 * 2. 异步批量写入MySQL
 * 3. 写入成功后删除Redis缓存
 * 4. 多线程并发处理，提高回写效率
 */
@Component
public class LogWriteBackTask {
    
    private static final Logger logger = LoggerFactory.getLogger(LogWriteBackTask.class);
    
    @Resource
    private LogCacheService logCacheService;
    
    @Resource
    private LogService logService;
    
    @Resource(name = "logWriteBackExecutor")
    private Executor logWriteBackExecutor;
    
    /**
     * 每批次处理的日志数量
     */
    private static final int BATCH_SIZE = 500;
    
    /**
     * 是否正在执行回写任务（防止重复执行）
     */
    private volatile boolean isRunning = false;
    
    /**
     * 最后一次执行结果
     */
    private volatile WriteBackResult lastResult = null;
    
    /**
     * 定时回写任务：每12小时执行一次
     * 
     * cron表达式：0 0 0,12 * * ?
     * - 每天的 00:00 和 12:00 执行
     */
    @Scheduled(cron = "0 0 0,12 * * ?")
    public void scheduledWriteBack() {
        logger.info("========== 定时日志回写任务开始 ==========");
        executeWriteBack();
    }
    
    /**
     * 手动触发回写任务（可以通过API调用）
     */
    public void manualWriteBack() {
        logger.info("========== 手动触发日志回写任务 ==========");
        executeWriteBack();
    }
    
    /**
     * 执行回写任务
     */
    private void executeWriteBack() {
        // 防止并发执行
        if (isRunning) {
            logger.warn("回写任务已在执行中，跳过本次调度");
            return;
        }
        
        try {
            isRunning = true;
            LocalDateTime startTime = LocalDateTime.now();
            
            // 获取所有待回写的日期
            List<String> pendingDates = logCacheService.getPendingDates();
            
            if (pendingDates.isEmpty()) {
                logger.info("没有需要回写的日志");
                return;
            }
            
            logger.info("发现 {} 个日期分组需要回写", pendingDates.size());
            
            // 统计信息
            AtomicInteger totalProcessed = new AtomicInteger(0);
            AtomicInteger totalSuccess = new AtomicInteger(0);
            AtomicInteger totalFailed = new AtomicInteger(0);
            
            // 使用CompletableFuture异步处理每个日期的日志
            CompletableFuture<?>[] futures = pendingDates.stream()
                .map(date -> CompletableFuture.runAsync(() -> {
                    try {
                        processDateLogs(date, totalProcessed, totalSuccess, totalFailed);
                    } catch (Exception e) {
                        logger.error("处理日期 {} 的日志时发生异常", date, e);
                    }
                }, logWriteBackExecutor))
                .toArray(CompletableFuture[]::new);
            
            // 等待所有任务完成
            CompletableFuture.allOf(futures).join();
            
            // 输出统计信息
            LocalDateTime endTime = LocalDateTime.now();
            long duration = java.time.Duration.between(startTime, endTime).getSeconds();
            
            logger.info("========== 日志回写任务完成 ==========");
            logger.info("处理日期分组: {}", pendingDates.size());
            logger.info("处理日志总数: {}", totalProcessed.get());
            logger.info("成功写入: {}", totalSuccess.get());
            logger.info("失败: {}", totalFailed.get());
            logger.info("耗时: {} 秒", duration);
            logger.info("========================================");
            
            // 保存执行结果
            lastResult = new WriteBackResult(
                totalProcessed.get(),
                totalSuccess.get(),
                totalFailed.get(),
                duration,
                endTime
            );
            
        } catch (Exception e) {
            logger.error("回写任务执行失败", e);
            // 保存失败结果
            lastResult = new WriteBackResult(0, 0, 0, 0, LocalDateTime.now());
            lastResult.setError(e.getMessage());
        } finally {
            isRunning = false;
        }
    }
    
    /**
     * 处理指定日期的日志
     * 
     * @param date 日期
     * @param totalProcessed 总处理数
     * @param totalSuccess 总成功数
     * @param totalFailed 总失败数
     */
    private void processDateLogs(String date, 
                                  AtomicInteger totalProcessed, 
                                  AtomicInteger totalSuccess, 
                                  AtomicInteger totalFailed) {
        try {
            logger.info("开始处理日期 {} 的日志", date);
            
            // 从Redis获取该日期的所有日志
            List<Log> cachedLogs = logCacheService.getCachedLogs(date);
            
            if (cachedLogs.isEmpty()) {
                logger.info("日期 {} 没有待回写的日志", date);
                return;
            }
            
            logger.info("日期 {} 共有 {} 条日志待回写", date, cachedLogs.size());
            totalProcessed.addAndGet(cachedLogs.size());
            
            // 分批处理，避免一次处理过多数据
            int successCount = 0;
            int failedCount = 0;
            
            for (int i = 0; i < cachedLogs.size(); i += BATCH_SIZE) {
                int endIndex = Math.min(i + BATCH_SIZE, cachedLogs.size());
                List<Log> batch = cachedLogs.subList(i, endIndex);
                
                try {
                    // 批量写入MySQL
                    boolean batchSuccess = logService.batchSaveLogsToDB(batch);
                    
                    if (batchSuccess) {
                        successCount += batch.size();
                        logger.debug("批次 [{}-{}] 写入成功", i, endIndex);
                    } else {
                        failedCount += batch.size();
                        logger.warn("批次 [{}-{}] 写入失败", i, endIndex);
                    }
                    
                } catch (Exception e) {
                    failedCount += batch.size();
                    logger.error("批次 [{}-{}] 写入异常", i, endIndex, e);
                }
                
                // 短暂休眠，避免对数据库造成过大压力
                if (endIndex < cachedLogs.size()) {
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
            
            totalSuccess.addAndGet(successCount);
            totalFailed.addAndGet(failedCount);
            
            // 如果全部成功，删除Redis缓存
            if (failedCount == 0) {
                logCacheService.deleteCachedLogs(date);
                logger.info("日期 {} 的日志全部回写成功，已清除Redis缓存", date);
            } else {
                logger.warn("日期 {} 的日志回写有失败，保留Redis缓存", date);
            }
            
        } catch (Exception e) {
            logger.error("处理日期 {} 的日志时发生异常", date, e);
        }
    }
    
    /**
     * 获取当前回写任务状态
     * 
     * @return 是否正在执行
     */
    public boolean isRunning() {
        return isRunning;
    }
    
    /**
     * 获取待回写日志统计
     * 
     * @return 统计信息
     */
    public String getPendingStatus() {
        try {
            List<String> dates = logCacheService.getPendingDates();
            long totalCount = logCacheService.getTotalCachedCount();
            
            return String.format("待回写日期: %d 个, 总日志数: %d 条", dates.size(), totalCount);
            
        } catch (Exception e) {
            logger.error("获取待回写状态失败", e);
            return "获取失败";
        }
    }
    
    /**
     * 获取最后一次执行结果
     * 
     * @return 执行结果
     */
    public WriteBackResult getLastResult() {
        return lastResult;
    }
    
    /**
     * 回写结果数据类
     */
    public static class WriteBackResult {
        private int totalProcessed;
        private int totalSuccess;
        private int totalFailed;
        private long durationSeconds;
        private LocalDateTime completeTime;
        private String error;
        
        public WriteBackResult(int totalProcessed, int totalSuccess, int totalFailed, 
                              long durationSeconds, LocalDateTime completeTime) {
            this.totalProcessed = totalProcessed;
            this.totalSuccess = totalSuccess;
            this.totalFailed = totalFailed;
            this.durationSeconds = durationSeconds;
            this.completeTime = completeTime;
        }
        
        public int getTotalProcessed() {
            return totalProcessed;
        }
        
        public int getTotalSuccess() {
            return totalSuccess;
        }
        
        public int getTotalFailed() {
            return totalFailed;
        }
        
        public long getDurationSeconds() {
            return durationSeconds;
        }
        
        public LocalDateTime getCompleteTime() {
            return completeTime;
        }
        
        public String getError() {
            return error;
        }
        
        public void setError(String error) {
            this.error = error;
        }
    }
}

